/**
 * model.cpp is part of %PROJECTNAME%.
 *
 * %PROJECTNAME% is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * %PROJECTNAME% is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with %PROJECTNAME%.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "model.h"
#include "cbs/task.h"
#include "../utils/visitor/visitor.h"
#include <sstream>
#include "cbs/constraints/constraint.h"
#include "cbs/resource.h"

const std::string Model::UNNAMED_MODEL = "UNNAMED";

Model::Model( const std::string & a_modelName ) :
    Visitable(),
    Printable(),
    m_tasks(),
    m_modelName( a_modelName ),
    m_constraints(),
    m_resources()
{
}

Model::~Model()
{
    Clear();
    m_modelName = "";
}

void Model::Clear()
{
    ClearTasks();
    ClearConstraints();
    ClearResources();
}

void Model::ClearTasks()
{
    tTasks::iterator it    = m_tasks.begin();
    tTasks::iterator itEnd = m_tasks.end();
    while( it != itEnd )
    {
        delete *it;
        ++it;
    }
}

void Model::ClearConstraints()
{
    tConstraints::iterator it    = m_constraints.begin();
    tConstraints::iterator itEnd = m_constraints.end();
    while( it != itEnd )
    {
        delete *it;
        ++it;
    }
    m_constraints.clear();
}

void Model::ClearResources()
{
    tResources::iterator it    = m_resources.begin();
    tResources::iterator itEnd = m_resources.end();
    while( it != itEnd )
    {
        delete *it;
        ++it;
    }
}

void Model::Accept( Visitor * a_visitor )
{
    if( a_visitor )
    {
        AcceptTasks( a_visitor );
        AcceptResources( a_visitor );
        AcceptConstraints( a_visitor );
        a_visitor->Visit( this );
    }
}

void Model::AcceptTasks( Visitor * a_visitor )
{
    tTasks::iterator it    = m_tasks.begin();
    tTasks::iterator itEnd = m_tasks.end();
    while( it != itEnd )
    {
        Task * task = *it;
        if( task )
            task->Accept( a_visitor );
        ++it;
    }
}

void Model::AcceptConstraints( Visitor * a_visitor )
{
    tConstraints::iterator it    = m_constraints.begin();
    tConstraints::iterator itEnd = m_constraints.end();
    while( it != itEnd )
    {
        Constraint * constraint = *it;
        if( constraint )
            constraint->Accept( a_visitor );
        ++it;
    }
}

void Model::AcceptResources( Visitor * a_visitor )
{
    tResources::iterator it    = m_resources.begin();
    tResources::iterator itEnd = m_resources.end();
    while( it != itEnd )
    {
        Resource * resource = *it;
        if( resource )
            resource->Accept( a_visitor );
        ++it;
    }
}

std::string Model::Print() const
{
    std::ostringstream strStream;
    strStream << "Model :" << m_modelName << std::endl;

    strStream << PrintTasks();
    strStream << PrintResources();
    strStream << PrintConstraints();

    return strStream.str();
}

std::string Model::PrintTasks() const
{
    std::ostringstream strStream;
    strStream << "Tasks :" << std::endl;

    tTasks::const_iterator it    = m_tasks.begin();
    tTasks::const_iterator itEnd = m_tasks.end();
    while( it != itEnd )
    {
        Task * task = *it;
        if( task )
            strStream << task->Print() << std::endl;
        ++it;
    }

    return strStream.str();
}

std::string Model::PrintConstraints() const
{
    std::ostringstream strStream;
    strStream << "Constraints :" << std::endl;

    tConstraints::const_iterator it    = m_constraints.begin();
    tConstraints::const_iterator itEnd = m_constraints.end();
    while( it != itEnd )
    {
        Constraint * constraint = *it;
        if( constraint )
            strStream << constraint->Print() << std::endl;
        ++it;
    }

    return strStream.str();
}

std::string Model::PrintResources() const
{
    std::ostringstream strStream;
    strStream << "Resources :" << std::endl;

    tResources::const_iterator it    = m_resources.begin();
    tResources::const_iterator itEnd = m_resources.end();
    while( it != itEnd )
    {
        Resource * resource = *it;
        if( resource )
            strStream << resource->Print() << std::endl;
        ++it;
    }

    return strStream.str();
}

void Model::Add( Task * a_task )
{
    if( a_task )
    {
        m_tasks.insert( a_task );
        Notify( ON_ADD_TASK_CALLBACK, a_task );
    }
}

void Model::Add( Constraint * a_constraint )
{
    if( a_constraint )
    {
        m_constraints.insert( a_constraint );
        Notify( ON_ADD_CONSTRAINT_CALLBACK, a_constraint );
    }
}

void Model::Add( Resource * a_resource )
{
    if( a_resource )
    {
        m_resources.insert( a_resource );
        Notify( ON_ADD_RESOURCE_CALLBACK, a_resource );
    }
}

void Model::ModelName( const std::string & a_modelName )
{
    m_modelName = a_modelName;
}

const std::string & Model::ModelName() const
{
    return m_modelName;
}

std::size_t Model::NTasks() const
{
    return m_tasks.size();
}

std::size_t Model::NResources() const
{
    return m_resources.size();
}

std::size_t Model::NConstraints() const
{
    return m_constraints.size();
}

void Model::Subscribe( tCallback a_callback, void * a_object, tModelCallback a_modelCallback )
{
    m_callbacks[ a_modelCallback ].push_back( std::make_pair( a_callback, a_object ) );
}

void Model::Notify( tModelCallback a_callback, void * a_param )
{
    tCallbacks::iterator it    = m_callbacks[ a_callback ].begin();
    tCallbacks::iterator itEnd = m_callbacks[ a_callback ].end();
    while( it != itEnd )
    {
        ( (*it).first ) ( (*it).second, a_param );
        ++it;
    }
}

